﻿using hyjx.comm;
using hyjx.comm.Exceptions;
using Hyjx.GF.Database;
using Hyjx.MixerService.Configs;
using Hyjx.MixerService.Exceptions;
using Hyjx.MixerService.Objects;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using S7.Net;
using Serilog;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using static System.Net.WebRequestMethods;

namespace Hyjx.MixerService
{
    public class MixerService
    {
        private int inConnect, inWriteMesOnline, inReportMixerStatus, inReportMixerAlarm, inStepStatusChanged, inPlcIsMesMode, inStepFinishedMesValid, inCollectionAlarmsForMes, inReportAlarmSys, inSaveMixerStatus;

        private bool _stepFinishedSignal;
        private int _mixerStepStatus;

        readonly DevicePlcConfig? _plc;
        readonly S7Protocol? _protocol;
        readonly IConfiguration? _config;
        readonly AppHttp? _http;
        readonly MaterialCodeConfig[]? _materialCodes;

        readonly string? _webApiBaseUrl;
        readonly string? _mixerSubmitAlarm;
        readonly string? _mixerRealStatus;
        readonly string? _mixerSubmitStatus;
        readonly int _realtimeDataBlock;
        readonly int _realtimeStartIndex;
        readonly int _realtimeDataLength;
        readonly string? _mesModeAddress;
        readonly short _stepStatus;
        readonly short _currentStepNumber;
        readonly short _readEndSymbleStart;
        readonly short _readDataBlock;
        readonly string? _realtimeMesMode;

        // 2025 年新增的 MES 报警上报接入配置
        readonly string? _alarmSysBaseUrl; // 报警上报基地址
        readonly string? _alarmUploadUrl; // 上传报警
        readonly AlarmSysPointsConfig _alarmSysPoint; // 报警系统点位
        readonly ConcurrentDictionary<int, List<UploadAlarmDetailCacheObject>> _alarmRecords = new(); // 采集到的报警信息集合
        readonly string? _alarmReportSettingFilePath; // 上报报警设置文件路径

        string? _dbConnectionString;
        string? _returnWaterTempAddr;

        /// <summary>
        /// MES在线
        /// </summary>
        public bool MesOnline { get; set; }
        /// <summary>
        /// 设备MES模式
        /// </summary>
        public bool IsMesMode { get; private set; }
        /// <summary>
        /// PLC是否已连接
        /// </summary>
        public bool IsConnected => _protocol!.IsConnected;
        /// <summary>
        /// PLC连接中
        /// </summary>
        public bool Connecting { get; private set; }

        public MixerService(IConfiguration config, DevicePlcConfig devicePlcConfig, S7Protocol protocol, AppHttp appHttp)
        {
            _config = config;
            _plc = devicePlcConfig;
            _protocol = protocol;
            _http = appHttp;

            _webApiBaseUrl = _config.GetValue<string>("WebApiBaseUrl");
            _mixerSubmitAlarm = _config.GetValue<string>("MixerSubmitAlarm");
            _mixerRealStatus = _config.GetValue<string>("MixerRealStatus");
            _mixerSubmitStatus = _config.GetValue<string>("MixerSubmitStatus");

            _realtimeDataBlock = _config.GetValue<int>("RecipePoint:RealtimeDataBlock");
            _realtimeStartIndex = _config.GetValue<int>("RecipePoint:RealtimeStartIndex");
            _realtimeDataLength = _config.GetValue<int>("RecipePoint:RealtimeDataLength");
            _mesModeAddress = _config["RecipePoint:WriteMesModeAddress"];
            _materialCodes = _config.GetSection("MaterialCodes").Get<MaterialCodeConfig[]>();
            _stepStatus = _config.GetValue<short>("RecipePoint:RealtimeStepStatus");
            _currentStepNumber = _config.GetValue<short>("RecipePoint:RealtimeStepNumber");
            _realtimeMesMode = _config["RecipePoint:RealtimeMesMode"];
            _readEndSymbleStart = _config.GetValue<short>("RecipePoint:ReadEndSymbleStart");
            _readDataBlock = _config.GetValue<short>("RecipePoint:ReadDataBlock");

            _alarmSysBaseUrl = _config.GetValue<string>("AlarmSysBaseUrl");
            _alarmUploadUrl = _config.GetValue<string>("AlarmUploadUrl");
            _alarmSysPoint = _config.GetSection("AlarmSysPoints").Get<AlarmSysPointsConfig>() ?? throw new ArgumentNullException(nameof(AlarmSysPointsConfig));
            _alarmReportSettingFilePath = _config.GetValue<string>("AlarmSettingFilePath");

            _returnWaterTempAddr = _config.GetValue<string>("ReturnWaterTemperatureAddress");

            string? dbPath = _config.GetValue<string>("ReportDbPath");
            string? dbPassword = _config.GetValue<string>("ReportDbPassword");
            if (string.IsNullOrEmpty(dbPath) || string.IsNullOrEmpty(dbPassword))
                Log.Error("读取数据库配置失败");
            else
                _dbConnectionString = $"Filename={dbPath};Password={dbPassword}";

            if (!string.IsNullOrEmpty(_dbConnectionString))
            {
                using GFDbContext db = new(_dbConnectionString);
                db.Database.Migrate();
            }
        }

        public void SaveMixerStatus()
        {
            if (Interlocked.Exchange(ref inSaveMixerStatus, 1) == 0)
            {
                try
                {
                    if (!string.IsNullOrEmpty(_dbConnectionString))
                    {
                        string deviceCode = _plc!.ControlDevice;
                        byte[] bytes = _protocol!.ReadBytes(_realtimeDataBlock, _realtimeStartIndex, _realtimeDataLength);

                        List<bool> bools = bytes[46].FromByteTo8Bool();
                        short materialCode = new byte[] { bytes[50], bytes[51] }.FromBytesToShort();
                        string materialName = string.Empty;

                        if (_materialCodes != null)
                        {
                            var material = _materialCodes.FirstOrDefault(p => p.FeatureType == _plc.FeatureType && p.HongYunValue == materialCode);
                            if (material != null)
                                materialName = material.Name;
                        }

                        MixerDataReportEntity entity = new()
                        {
                            ScatteredRotateSpeed = new byte[] { bytes[0], bytes[1] }.FromBytesToShort(),
                            ScatteredRotateActualSpeed = (float)Math.Round(new byte[] { bytes[2], bytes[3], bytes[4], bytes[5] }.FromBytesToFloat(), 3),
                            StirRotateSpeed = new byte[] { bytes[6], bytes[7] }.FromBytesToShort(),
                            StirRotateActualSpeed = (float)Math.Round(new byte[] { bytes[8], bytes[9], bytes[10], bytes[11] }.FromBytesToFloat(), 3),
                            ElapsedTime = new byte[] { bytes[12], bytes[13] }.FromBytesToShort(),
                            RemainTime = new byte[] { bytes[14], bytes[15] }.FromBytesToShort(),
                            ActualTemperature = (float)Math.Round(new byte[] { bytes[16], bytes[17], bytes[18], bytes[19] }.FromBytesToFloat(), 3),
                            ActualPressure = (float)Math.Round(new byte[] { bytes[20], bytes[21], bytes[22], bytes[23] }.FromBytesToFloat() / 1000, 3),
                            StirCurrent = (float)Math.Round(new byte[] { bytes[28], bytes[29], bytes[30], bytes[31] }.FromBytesToFloat(), 3),
                            ScatteredCurrent = (float)Math.Round(new byte[] { bytes[32], bytes[33], bytes[34], bytes[35] }.FromBytesToFloat(), 3),
                            Temperature = (float)Math.Round(new byte[] { bytes[36], bytes[37], bytes[38], bytes[39] }.FromBytesToFloat(), 3),
                            Pressure = (float)Math.Round(new byte[] { bytes[40], bytes[41], bytes[42], bytes[43] }.FromBytesToFloat() / 1000, 3),
                            Step = new byte[] { bytes[44], bytes[45] }.FromBytesToShort() == 2 ? new byte[] { bytes[64], bytes[65] }.FromBytesToShort() : new byte[] { bytes[48], bytes[49] }.FromBytesToShort(),
                            MaterialWeight = (float)Math.Round(new byte[] { bytes[52], bytes[53], bytes[54], bytes[55] }.FromBytesToFloat(), 3),
                            MaterialActualWeight = (float)Math.Round(new byte[] { bytes[56], bytes[57], bytes[58], bytes[59] }.FromBytesToFloat(), 3),
                            ReturnWaterTemperature = (float)Math.Round(_protocol.ReadFloat(_returnWaterTempAddr), 3),
                            Material = materialName,
                            DeviceName = _plc!.DeviceName,
                            DeviceCode = deviceCode,
                            RecordTime = DateTime.Now
                        };

                        // 对某些负数上报，做出置 0 处理
                        if (entity.RemainTime < 0)
                            entity.RemainTime = 0;

                        using GFDbContext context = new(_dbConnectionString);
                        context.MixerDataReports.Add(entity);
                        context.SaveChanges();

                        Log.Information($"设备编码:{_plc!.ControlDevice},保存过程参数成功");
                    }
                }
                catch (TryAddToProtocolsException ex)
                {
                    Log.Error(ex, $"无法连接到 PLC,设备编码：{_plc!.ControlDevice},IP:{_plc.Ip}");
                }
                catch (Exception ex)
                {
                    Log.Error(ex, $"207 设备编码：{_plc!.ControlDevice},IP:{_plc.Ip} 保存设备状态数据出错");
                }
                finally
                {
                    Interlocked.Exchange(ref inSaveMixerStatus, 0);
                }
            }
        }
        /// <summary>
        /// 上报设备过程参数
        /// </summary>
        public async void ReportMixerStatus()
        {
            if (Interlocked.Exchange(ref inReportMixerStatus, 1) == 0)
            {
                try
                {
                    ReportMixerStatusArgs? args = GetMixerStatus();
                    if (args == null || args.Step == 0 || args.StepStatus == 3) // 工步号 == 0，客户要求不上报
                        return;

                    if (MesOnline)
                    {
                        var result = await _http!.SendMixerRealStatus(_webApiBaseUrl + _mixerRealStatus ?? throw new ArgumentNullException(nameof(_webApiBaseUrl)), args); // 发送请求并记录日志
                    }
                }
                catch (TryAddToProtocolsException ex)
                {
                    Log.Error(ex, $"无法连接到 PLC,设备编码：{_plc!.ControlDevice},IP:{_plc.Ip}");
                }
                catch (Exception ex)
                {
                    Log.Error(ex, $"207 设备编码：{_plc!.ControlDevice},IP:{_plc.Ip} 状态上报出错");
                }
                finally
                {
                    Interlocked.Exchange(ref inReportMixerStatus, 0);
                }
            }
        }
        /// <summary>
        /// 上报设备报警信息
        /// </summary>
        public async void ReportMixerAlarm()
        {
            if (Interlocked.Exchange(ref inReportMixerAlarm, 1) == 0)
            {
                try
                {
                    var bytes = _protocol!.ReadBytes(_realtimeDataBlock, _realtimeStartIndex, _realtimeDataLength);
                    List<bool> firstBits = bytes[24].FromByteTo8Bool();
                    List<bool> secondBits = bytes[25].FromByteTo8Bool();

                    ReportMixerAlarmArgs args = new()
                    {
                        DeviceCode = _plc!.ControlDevice,
                        ScatteredAlarm = firstBits[0],
                        StirAlarm = firstBits[1],
                        VacuumAlarm = firstBits[2],
                        TemperatureAlarm = firstBits[3],
                        MotorOverloadedAlarm = firstBits[4],
                        StopState = firstBits[5],
                        OvertemperatureAlarm = firstBits[6],
                        ColdWaterFlowAlarm = firstBits[7],
                        HighspeedShaftTimeout = secondBits[0],
                        LowspeedShaftTimeout = secondBits[1],
                        UnderpressureAlarm = secondBits[2],
                        BackwaterAlarm = secondBits[3],
                        OverpressureAlarm = secondBits[4],
                        PressureGaugeAlarm = secondBits[6]
                    };

                    // 发现有报警，才上报给 MES
                    if (args.ScatteredAlarm || args.StirAlarm || args.VacuumAlarm || args.TemperatureAlarm || args.MotorOverloadedAlarm || args.StopState ||
                    args.OvertemperatureAlarm || args.ColdWaterFlowAlarm || args.HighspeedShaftTimeout || args.LowspeedShaftTimeout || args.UnderpressureAlarm ||
                    args.BackwaterAlarm || args.OverpressureAlarm || args.PressureGaugeAlarm)
                    {
                        if (MesOnline) // Mes 在线，才上报
                        {
                            var result = await _http!.SendMixerAlarm(_webApiBaseUrl + _mixerSubmitAlarm ?? throw new ArgumentNullException(nameof(_webApiBaseUrl)), args); // 发送请求并记录日志
                        }
                    }
                }
                catch (TryAddToProtocolsException ex)
                {
                    Log.Error(ex, $"无法连接到 PLC,设备编码：{_plc!.ControlDevice},IP:{_plc.Ip}");
                }
                catch (S7ProtocolReadException ex)
                {
                    Log.Error(ex, "206 读取搅拌机警报异常。");
                }
                catch (Exception ex)
                {
                    Log.Error(ex, "206 上报搅拌机警报异常。");
                }
                finally
                {
                    Interlocked.Exchange(ref inReportMixerAlarm, 0);
                }
            }
        }
        /// <summary>
        /// 写入PLC MES在线信号
        /// </summary>
        public void WriteMesOnline()
        {
            if (Interlocked.Exchange(ref inWriteMesOnline, 1) == 0)
            {
                try
                {

                    _protocol!.Write(_mesModeAddress, MesOnline);
                    if (!MesOnline)
                        Log.Error($"MES在线:{MesOnline}");

                }
                catch (TryAddToProtocolsException ex)
                {
                    Log.Error(ex, $"无法连接到 PLC,设备编码：{_plc!.ControlDevice},IP:{_plc.Ip}");
                }
                catch (Exception ex)
                {
                    Log.Error(ex, $"207 设备编码：{_plc!.ControlDevice},IP:{_plc.Ip} 写MES在线异常");
                }
                finally
                {
                    Interlocked.Exchange(ref inWriteMesOnline, 0);
                }
            }
        }
        /// <summary>
        /// 读取设备MES模式
        /// </summary>
        public void PlcIsMesMode()
        {
            if (Interlocked.Exchange(ref inPlcIsMesMode, 1) == 0)
            {
                try
                {

                    IsMesMode = _protocol!.ReadBool(_plc!.MesMode);

                }
                catch (Exception err)
                {
                    Log.Error($"设备IP:{_protocol!.Ip} 读取MES模式异常,信息:{err.Message}");
                }
                finally
                {
                    Interlocked.Exchange(ref inPlcIsMesMode, 0);
                }
            }
        }
        /// <summary>
        /// 工步状态变化上报过程参数
        /// </summary>
        public async void StepStatusChanged()
        {
            if (Interlocked.Exchange(ref inStepStatusChanged, 1) == 0)
            {
                try
                {
                    var stepStatus = _protocol!.ReadInt($"DB{_realtimeDataBlock}.DBW{_stepStatus}");
                    Log.Information($"设备编码:{_plc!.ControlDevice},工步完成状态值:{stepStatus}");
                    if (stepStatus == _mixerStepStatus || stepStatus == 0 || stepStatus == 3) return;

                    ReportMixerStatusArgs? args = GetMixerStatus();
                    Log.Information($"设备编码:{_plc.ControlDevice},当前工步号:{args?.Step},设备状态值:{args?.StepStatus},上位机状态值:{_mixerStepStatus}");
                    if (args is null || (args.Step == 0 && args.StepStatus != 2)) return;

                    if (args.Step == 0 && args.StepStatus == 2) // 所有工步完成时，工步号值拿到的会是 0，需要在 工步 End 设定点位里，找到值为 1 的，该项则为最大工步号
                    {
                        byte[] endStepBytes = _protocol.ReadBytes(_readDataBlock, _readEndSymbleStart, 3);
                        List<bool> endSteps = endStepBytes.FromBytesToBools();
                        var endStep = endSteps.FirstOrDefault(p => p == true);
                        int maxStep = endSteps.IndexOf(endStep) + 1;
                        args.Step = (short)maxStep;
                    }

                    if (MesOnline)
                    {
                        var result = await _http!.SendMixerStatus(_webApiBaseUrl + _mixerSubmitStatus ?? throw new ArgumentNullException(nameof(_webApiBaseUrl)), args);
                        if (result is not null)
                        {
                            if (args.StepStatus == 2 && result.Code == 0)
                            {
                                var okAddr = $"DB{_realtimeDataBlock}.DBX62.1";
                                var okOld = _protocol.ReadBool(okAddr);
                                Log.Information($"设备编码:{_plc.ControlDevice} 工步:{args.Step}完成状态信号点位:{okAddr},值:{okOld}");
                                if (okOld)
                                {
                                    bool okNew = okOld;
                                    for (int i = 0; i < 3; i++)
                                    {
                                        _protocol.Write(okAddr, false);
                                        await Task.Delay(TimeSpan.FromMilliseconds(120)).ConfigureAwait(false);
                                        okNew = _protocol.ReadBool(okAddr);
                                        if (!okNew) break;
                                    }

                                    Log.Information($"设备编码:{_plc.ControlDevice} 复位工步{args.Step}完成状态信号点位:{okAddr},值:{okOld}-->{okNew}");
                                }
                            }

                            _mixerStepStatus = stepStatus;
                        }
                        else
                        {
                            Log.Error($"设备编码:{_plc.ControlDevice}调用MES 205接口返回为空");
                        }
                    }

                }
                catch (TryAddToProtocolsException ex)
                {
                    Log.Error(ex, $"无法连接到 PLC,设备编码：{_plc!.ControlDevice},IP:{_plc.Ip}");
                }
                catch (Exception ex)
                {
                    Log.Error(ex, "205 上报搅拌机状态变更异常。");
                }
                finally
                {
                    Interlocked.Exchange(ref inStepStatusChanged, 0);
                }
            }
        }
        /// <summary>
        /// 工步完成MES校验
        /// </summary>
        /// <exception cref="ArgumentNullException"></exception>
        public async void StepFinishedMesValid()
        {
            if (Interlocked.Exchange(ref inStepFinishedMesValid, 1) == 0)
            {
                try
                {
                    var stepFinishedSignal = _protocol!.ReadBool(_plc!.StepSignal ?? throw new ArgumentNullException(nameof(_plc.StepSignal)));
                    if (stepFinishedSignal && stepFinishedSignal != _stepFinishedSignal)
                    {
                        Log.Information($"设备编码:{_plc.ControlDevice} 工步完成MES校验信号点位:{_plc.StepSignal},值:{_stepFinishedSignal}-->{stepFinishedSignal}");

                        ReportMixerStatusArgs? args = GetMixerStatus();

                        Log.Information($"设备编码:{_plc.ControlDevice} 工步号:{args?.Step},工步状态值:{args?.StepStatus}");

                        if (args is null /*|| args.StepStatus == 0 || args.StepStatus == 3 */ || (args.Step == 0 && args.StepStatus != 2)) return;

                        if (args.Step == 0 && args.StepStatus == 2) // 所有工步完成时，工步号值拿到的会是 0，需要在 工步 End 设定点位里，找到值为 1 的，该项则为最大工步号
                        {
                            byte[] endStepBytes = _protocol.ReadBytes(_readDataBlock, _readEndSymbleStart, 3);
                            List<bool> endSteps = endStepBytes.FromBytesToBools();
                            var endStep = endSteps.FirstOrDefault(p => p == true);
                            int maxStep = endSteps.IndexOf(endStep) + 1;
                            args.Step = (short)maxStep;
                        }

                        if (MesOnline)
                        {
                            var result = await _http!.SendMixerStatus(_webApiBaseUrl + _mixerSubmitStatus ?? throw new ArgumentNullException(nameof(_webApiBaseUrl)), args);
                            if (result is not null)
                            {
                                if (args.StepStatus == 2 && result.Code == 0)
                                {
                                    var okAddr = $"DB{_realtimeDataBlock}.DBX62.1";
                                    var okOld = _protocol.ReadBool(okAddr);
                                    Log.Information($"设备编码:{_plc.ControlDevice} 工步{args.Step}完成状态信号点位:{okAddr},值:{okOld}");
                                    var okNew = okOld;
                                    for (int i = 0; i < 3; i++)
                                    {
                                        _protocol.Write(okAddr, false);
                                        await Task.Delay(TimeSpan.FromMilliseconds(120)).ConfigureAwait(false);
                                        okNew = _protocol.ReadBool(okAddr);
                                        if (!okNew) break;
                                    }
                                    Log.Information($"设备编码:{_plc.ControlDevice} 工步{args.Step}完成状态信号:{okAddr},值:{okOld}-->{okNew}");
                                }
                                else
                                {
                                    if (result.Message.Contains("已经完成了"))
                                    {
                                        var okAddr = $"DB{_realtimeDataBlock}.DBX62.1";
                                        var okOld = _protocol.ReadBool(okAddr);
                                        Log.Information($"设备编码:{_plc.ControlDevice} 工步{args.Step}完成状态信号点位:{okAddr},值:{okOld}");
                                        var okNew = okOld;
                                        for (int i = 0; i < 3; i++)
                                        {
                                            _protocol.Write(okAddr, false);
                                            await Task.Delay(TimeSpan.FromMilliseconds(120)).ConfigureAwait(false);
                                            okNew = _protocol.ReadBool(okAddr);
                                            if (!okNew) break;
                                        }
                                        Log.Information($"设备编码:{_plc.ControlDevice} 工步{args.Step}完成状态信号:{okAddr},值:{okOld}-->{okNew}");
                                    }
                                }
                            }
                            else
                            {
                                Log.Error($"设备编码:{_plc.ControlDevice}调用MES 205接口返回为空");
                            }
                        }
                    }

                    if (stepFinishedSignal != _stepFinishedSignal)
                    {
                        _stepFinishedSignal = stepFinishedSignal;
                        Log.Information($"设备编码:{_plc.ControlDevice} 工步完成MES校验信号点位:{_plc.StepSignal},值:{_stepFinishedSignal}-->{stepFinishedSignal}");
                    }
                }
                catch (TryAddToProtocolsException ex)
                {
                    Log.Error(ex, $"无法连接到 PLC,设备编码：{_plc!.ControlDevice},IP:{_plc.Ip}");
                }
                catch (Exception ex)
                {
                    Log.Error(ex, "工步完成MES校验异常。");
                }
                finally
                {
                    Interlocked.Exchange(ref inStepFinishedMesValid, 0);
                }
            }
        }

        /// <summary>
        /// 为 MES 采集报警
        /// </summary>
        public void CollectionAlarmsForMes()
        {
            if (Interlocked.Exchange(ref inCollectionAlarmsForMes, 1) == 0)
            {
                try
                {
                    byte[] bytes = _protocol!.ReadBytes(_alarmSysPoint!.Summary!.DbNumber, _alarmSysPoint.Summary.StartPoint, _alarmSysPoint.Summary.Length);

                    int intergerIndex = 0;
                    foreach (byte b in bytes)
                    {
                        List<bool> bools = b.FromByteTo8Bool(); // 字节的八个比特位
                        int decimalIndex = 0;
                        foreach (bool bl in bools)
                        {
                            AlarmSysDetailConfig? alarm = _alarmSysPoint?.Details?.FirstOrDefault(p => p.PointInteger == intergerIndex && p.PointDecimal == decimalIndex);
                            decimalIndex++;

                            if (alarm == null)
                                continue;

                            DateTime now = DateTime.Now;
                            UploadAlarmDetailCacheObject newAlarm = new() { AlarmDate = now, Code = alarm.Code, Name = alarm.Name };
                            bool getRecords = _alarmRecords.TryGetValue(alarm.Code, out List<UploadAlarmDetailCacheObject>? records);
                            if (!getRecords || records == null) // 缓存里没有这个报警代码记录
                            {
                                if (bl) // 出现报警
                                    _alarmRecords.TryAdd(alarm.Code, new List<UploadAlarmDetailCacheObject> { newAlarm });
                            }
                            else // 缓存里有这个报警代码记录
                            {
                                UploadAlarmDetailCacheObject? last = records.LastOrDefault(); // 找到最后一条记录
                                if (last == null) // 缓存里有报警代码但没数据
                                {
                                    if (bl)
                                    {
                                        records.Add(newAlarm);
                                    }
                                }
                                else // 缓存里报警代码里有数据
                                {
                                    if (!bl) // 当前不报警
                                    {
                                        if (last.EndDate == null) // 此条报警记录还没有结束时间
                                        {
                                            last.EndDate = now;
                                            last.Value = (int)(last.EndDate.Value - last.AlarmDate).TotalSeconds;
                                        }
                                        continue;
                                    }

                                    // 处理当前出现报警的情况
                                    if (last.EndDate != null)
                                    {
                                        records.Add(newAlarm);
                                    }

                                }
                            }
                        }
                        intergerIndex++;
                    }
                }
                catch (TryAddToProtocolsException ex)
                {
                    Log.Error(ex, $"无法连接到 PLC,设备编码：{_plc!.ControlDevice},IP:{_plc.Ip}");
                }
                catch (S7ProtocolReadBytesException ex)
                {
                    Log.Error(ex, "采集报警集合读取 PLC 字节数组异常。");
                }
                catch (Exception ex)
                {
                    Log.Error(ex, "采集报警集合异常。");
                }
                finally
                {
                    Interlocked.Exchange(ref inCollectionAlarmsForMes, 0);
                }
            }
        }

        /// <summary>
        /// 上报设备报警
        /// </summary>
        public async Task ReportAlarmSys(string lineName, bool isPositive)
        {
            if (Interlocked.Exchange(ref inReportAlarmSys, 1) == 0)
            {
                try
                {
                    if (!MesOnline)
                        return;

                    //StreamWriteText($"#{DateTime.Now}: 准备检查有没有报警记录要发送 671 数据给 MES");
                    if (!_alarmRecords.Any())
                    {
                        //StreamWriteText($"#{DateTime.Now}: 没有任何报警记录要发送 671 数据给 MES");
                        return;
                    }

                    if (string.IsNullOrEmpty(_alarmReportSettingFilePath))
                        return;

                    string json = System.IO.File.ReadAllText(_alarmReportSettingFilePath);
                    AlarmReportSysSettings? settings = JsonSerializer.Deserialize<AlarmReportSysSettings>(json);
                    if (settings == null || settings.Settings == null || !settings.Settings.Any())
                        return;

                    List<UploadAlarmDetailJObject> uploadAlarms = new();
                    string dateFormate = "yyyy-MM-dd HH:mm:ss";
                    foreach (var alarm in _alarmRecords)
                    {
                        foreach (var al in alarm.Value)
                        {
                            uploadAlarms.Add(new UploadAlarmDetailJObject
                            {
                                AlarmDate = al.AlarmDate.ToString(dateFormate),
                                Code = al.Code.ToString(),
                                EndDate = al.EndDate == null ? string.Empty : al.EndDate.Value.ToString(dateFormate),
                                Name = al.Name,
                                Value = al.Value == 0 ? string.Empty : al.Value.ToString()
                            });
                        }
                    }
                    AlarmReportSysSetting set = settings.Settings!.First(p => p.LineName == lineName);
                    UploadWarnStatusArgs args = new()
                    {
                        AlarmTime = DateTime.Now.ToString(dateFormate),
                        BatchNo = set.ProduceBatch,
                        CmdID = 671,
                        EID = _plc!.ControlDevice,
                        IP = _plc!.Ip,
                        LineName = lineName,
                        OperatorUser = settings.Operator,
                        ProductCode = set.ProductionCode,
                        TransferType = 0,
                        StepName = isPositive == true ? "正极搅拌" : "负极搅拌",
                        Data = uploadAlarms.ToArray()
                    };
                    //StreamWriteText($"#{DateTime.Now}: 设备编码:{_plc.ControlDevice} 准备发送 671 数据给 MES");
                    var result = await _http!.UploadWarnStatusAsync($"{_alarmSysBaseUrl}{_alarmUploadUrl}", args);

                    if (result == null)
                    {
                        Log.Error($"设备编码:{_plc.ControlDevice}调用MES 671 接口返回为空");
                        return;
                    }

                    if (result.Status != 1)
                    {
                        Log.Error($"设备编码:{_plc.ControlDevice}调用MES 671 接口返回失败，{result.Msg}");
                        return;
                    }
                    //StreamWriteText($"#{DateTime.Now}: 设备编码:{_plc.ControlDevice}调用MES 671 接口成功。");
                    Log.Information($"设备编码:{_plc.ControlDevice}调用MES 671 接口成功。");
                    _alarmRecords.Clear();
                }
                catch (TryAddToProtocolsException ex)
                {
                    Log.Error(ex, $"无法连接到 PLC,设备编码：{_plc!.ControlDevice},IP:{_plc.Ip}");
                }
                catch (Exception ex)
                {
                    Log.Error(ex, "上报设备报警异常。");
                }
                finally
                {
                    Interlocked.Exchange(ref inReportAlarmSys, 0);
                }
            }
        }


        /// <summary>
        /// 读取设备过程参数
        /// </summary>
        /// <returns></returns>
        private ReportMixerStatusArgs? GetMixerStatus()
        {
            try
            {
                string deviceCode = _plc!.ControlDevice;
                byte[] bytes = _protocol!.ReadBytes(_realtimeDataBlock, _realtimeStartIndex, _realtimeDataLength);

                List<bool> bools = bytes[46].FromByteTo8Bool();
                short materialCode = new byte[] { bytes[50], bytes[51] }.FromBytesToShort();

                ReportMixerStatusArgs args = new()
                {
                    DeviceCode = deviceCode,
                    ScatteredRotateSpeed = new byte[] { bytes[0], bytes[1] }.FromBytesToShort(),
                    ScatteredRotateActualSpeed = (float)Math.Round(new byte[] { bytes[2], bytes[3], bytes[4], bytes[5] }.FromBytesToFloat(), 3),
                    StirRotateSpeed = new byte[] { bytes[6], bytes[7] }.FromBytesToShort(),
                    StirRotateActualSpeed = (float)Math.Round(new byte[] { bytes[8], bytes[9], bytes[10], bytes[11] }.FromBytesToFloat(), 3),
                    ElapsedTime = new byte[] { bytes[12], bytes[13] }.FromBytesToShort(),
                    RemainTime = new byte[] { bytes[14], bytes[15] }.FromBytesToShort(),
                    ActualTemperature = (float)Math.Round(new byte[] { bytes[16], bytes[17], bytes[18], bytes[19] }.FromBytesToFloat(), 3),
                    ActualVacuum = (float)Math.Round(new byte[] { bytes[20], bytes[21], bytes[22], bytes[23] }.FromBytesToFloat() / 1000, 3),
                    StirCurrent = (float)Math.Round(new byte[] { bytes[28], bytes[29], bytes[30], bytes[31] }.FromBytesToFloat(), 3),
                    ScatteredCurrent = (float)Math.Round(new byte[] { bytes[32], bytes[33], bytes[34], bytes[35] }.FromBytesToFloat(), 3),
                    Temperature = (float)Math.Round(new byte[] { bytes[36], bytes[37], bytes[38], bytes[39] }.FromBytesToFloat(), 3),
                    Vacuum = (float)Math.Round(new byte[] { bytes[40], bytes[41], bytes[42], bytes[43] }.FromBytesToFloat() / 1000, 3),
                    StepStatus = new byte[] { bytes[44], bytes[45] }.FromBytesToShort(),
                    RecipeRunning = bools[0],
                    MixerDischarge = bools[1],
                    EcMesValid = bools[2],
                    MesMode = bools[3],
                    //Step = new byte[] { bytes[48], bytes[49] }.FromBytesToShort(),
                    Step = new byte[] { bytes[44], bytes[45] }.FromBytesToShort() == 2 ? new byte[] { bytes[64], bytes[65] }.FromBytesToShort() : new byte[] { bytes[48], bytes[49] }.FromBytesToShort(),
                    MaterialCode = materialCode,
                    MaterialWeight = (float)Math.Round(new byte[] { bytes[52], bytes[53], bytes[54], bytes[55] }.FromBytesToFloat(), 3),
                    MaterialActualWeight = (float)Math.Round(new byte[] { bytes[56], bytes[57], bytes[58], bytes[59] }.FromBytesToFloat(), 3),
                    Status = new byte[] { bytes[26], bytes[27] }.FromBytesToShort() switch
                    {
                        1 => "运行",
                        2 => "待机",
                        _ => "故障",
                    }
                };

                // 对某些负数上报，做出置 0 处理
                if (args.RemainTime < 0)
                    args.RemainTime = 0;

                if (_materialCodes != null)
                {
                    var materials = _materialCodes.Where(p => p.ToleranceLowerLimitAddress != null || p.ToleranceLowerLimitAddress != string.Empty);
                    List<MaterialTolerance> tolerances = new();
                    foreach (var item in materials)
                    {
                        // 将红运原材料编码改为赣锋原材料编码
                        if (item.HongYunValue == args.MaterialCode)
                            args.MaterialCode = item.GanFengValue;

                        // 填充原材料公差数组
                        if (string.IsNullOrEmpty(item.ToleranceLowerLimitAddress) || string.IsNullOrEmpty(item.ToleranceLowerLimitAddress))
                            continue;

                        if (_plc.FeatureType != item.FeatureType) continue;

                        tolerances.Add(new MaterialTolerance
                        {
                            Code = item.GanFengValue,
                            Name = item.Name,
                            LowerLimit = _protocol.ReadFloat(item.ToleranceLowerLimitAddress),
                            UpperLimit = _protocol.ReadFloat(item.ToleranceUpperLimitAddress)
                        });
                    }
                    args.Tolerances = tolerances.ToArray();
                }
                return args;
            }
            catch (S7ProtocolReadException ex)
            {
                Log.Error(ex, "读取 PLC 过程数据出错");
                return null;
            }
            catch (Exception ex)
            {
                Log.Error(ex, "读取 PLC 过程数据出错");
                return null;
            }
        }

        public void Connect()
        {
            if (Interlocked.Exchange(ref inConnect, 1) == 0)
            {
                try
                {
                    if (!_protocol!.IsConnected)
                    {
                        Connecting = true;
                        _protocol.Connect();
                    }
                }
                catch (S7ProtocolConnectException ex)
                {
                    Log.Error($"无法连接到设备编码:{_plc!.ControlDevice} IP:{_plc.Ip} 异常:{ex.Message}");
                }
                finally
                {
                    Connecting = false;
                    Interlocked.Exchange(ref inConnect, 0);
                }
            }
        }
        public void Disconnect()
        {
            _protocol?.Dispose();
        }

        void StreamWriteText(string content)
        {
            try
            {
                string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "TestTempLog.txt");
                using StreamWriter stream = new(path, true, Encoding.UTF8);
                stream.WriteLine(content);
            }
            catch (Exception ex)
            {

            }
        }
    }
}
